package svc.elib.linkpred;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;

import edu.uci.ics.jung.graph.UndirectedSparseGraph;

import svc.elib.db.Author;
import svc.elib.db.Database;
import svc.elib.socnet.ConnectedComponents;
import svc.elib.socnet.EvolutionarySnapshots;
import svc.elib.socnet.Link;
import svc.elib.socnet.Net;
import svc.elib.socnet.SocConstructor;

public class LPEngine {
	
	private Net prev;
	private Net next;
	private LPMetric metric;
	private int numNewLinks;
	
	private class PLink implements Comparable<PLink> {
		String key;
		double score;
		
		public PLink(String key, double score) {
			this.key = key;
			this.score = score;
		}
		
		@Override
		public int compareTo(PLink o) {
			if  (o.score > this.score)
				return 1;
			else
			if (o.score < this.score)
				return -1;
			else
				return 0;
		}
		
		public String toString() {
			return score + ": " + key;
		}
	}
	
	private LinkedList<PLink> predictedLinks = new LinkedList<PLink>();
	private HashSet<String> newLinkSet = new HashSet<String>();
	
	public LPEngine(Net prev, Net next, LPMetric metric) {
		this.prev = prev;
		this.next = next;
		this.metric = metric;
	}
	
	public double predictLinks() {
		Iterator<Author> ait = prev.getGraph().getVertices().iterator();
		LinkedList<Author> aList = new LinkedList<Author>();
		while (ait.hasNext()) {
			Author a = ait.next();
			aList.addLast(a);
		}
		
		UndirectedSparseGraph<Author, Link> prevGraph = prev.getGraph();
		for (int i = 1; i < aList.size(); i++) {
			for (int j = 0; j < i; j++) {
				Author a1 = aList.get(j);
				Author a2 = aList.get(i);
				if (!connected(a1, a2, prevGraph)) {
					double s = metric.score(a1, a2, prev);
					predictedLinks.add(new PLink(formKey(a1, a2), s));
				}
			}
		}
		
		Collections.sort(predictedLinks);
		
		Iterator<Link> lit = next.getGraph().getEdges().iterator();
		while (lit.hasNext()) {
			Link l = lit.next();
			Author a1 = l.getSrc();
			Author a2 = l.getDst();
			if (prevGraph.containsVertex(a1) && prevGraph.containsVertex(a2)) {
				if (!connected(a1, a2, prevGraph)) {
					newLinkSet.add(formKey(a1, a2));
				}
			}
		}
		
		numNewLinks = newLinkSet.size();
		int predicted = 0;
		for (int i = 0; i < numNewLinks; i++) {
			String predictedLinkKey = predictedLinks.get(i).key;
			if (newLinkSet.contains(predictedLinkKey)) {
				++predicted;
			}
		}
		
		return (double) predicted / (double) numNewLinks;
	}
	
	private boolean connected(Author a1, Author a2, UndirectedSparseGraph<Author, Link> g) {
		return g.findEdge(a1, a2) != null || g.findEdge(a2, a1) != null; // BIG QUESTION (UNDIRECTED GRAPHS)
	}
	
	private String formKey(Author a1, Author a2) {
		String a1Name = a1.getName();
		String a2Name = a2.getName();
		if (a1Name.compareTo(a2Name) < 0)
			return a1Name + "_" + a2Name;
		else
			return a2Name + "_" + a1Name;
	}
	
	public static void main(String[] args) 
		throws IOException
	{
		Database db = new Database("eLibData.csv", 1932, 2011);
		System.out.println(db.info());
		SocConstructor soc = new SocConstructor(db);
		Net net = soc.getNet();
		net.printInfo();
		
		ConnectedComponents sc = new ConnectedComponents(net);
		sc.resolveComponents();
		net = sc.getComponents().get(0);
		
		EvolutionarySnapshots es = new EvolutionarySnapshots(net);
		Net[] snaps = es.getSnapshots();
		int startYear = es.getStartYear();
		
		for (int i = 0; i < snaps.length - 2; i++) {
			Net prev = snaps[i];
			//Net next = snaps[i + 5];
			Net next = snaps[snaps.length - 1];
			
			LPEngine lpe = new LPEngine(prev, next, new PrefAttachMetric());
			double scorePF = lpe.predictLinks();
			
			lpe = new LPEngine(prev, next, new CommonNeighbours());
			double scoreCN = lpe.predictLinks();
			
			lpe = new LPEngine(prev, next, new Jaccard());
			double scoreJ = lpe.predictLinks();
			
			lpe = new LPEngine(prev, next, new Distance());
			double scoreD = lpe.predictLinks();
			
			lpe = new LPEngine(prev, next, new Salton());
			double scoreS = lpe.predictLinks();
			
			lpe = new LPEngine(prev, next, new AdamicAdar());
			double scoreAA = lpe.predictLinks();
			
			System.out.println((startYear + i) + ", score = " + scorePF + ", " + scoreCN + ", " + scoreJ + ", " + scoreD + ", " 
			                                                  + scoreS  + ", " + scoreAA);
		}
	}
}
